#include "drivers/chip/uart.h"
#include "drivers/chip/irq.h"

#include "drivers/chip/gpio.h"
#include "drivers/chip/clk.h"
#include "sys_types.h"

#include "family_stm32f1.h"
#include "remap_stm32f1.h"

#include "stm32f1xx.h"
#include "stm32f1xx_hal_rcc.h"
#include "stm32f1xx_hal_dma.h"
#include "stm32f1xx_hal_usart.h"

#define SYS_LOG_DOMAIN "hal"
#include "sys_log.h"

#define DEV (id2dev[id])
#define IRQ (id2irqn[id])
#define GPIO (id2gpio[pin / 16])

static USART_TypeDef *const id2dev[] = {
    [1] = USART1,
    [2] = USART2,

#if (FAMILY_STM32F1 > 1)
    [3] = USART3,
#endif

#if (FAMILY_STM32F1 > 2)
    [4] = UART4,
    [5] = UART5,
#endif
};

static IRQn_Type const id2irqn[] = {
    [1] = USART1_IRQn,
    [2] = USART2_IRQn,

#if (FAMILY_STM32F1 > 1)
    [3] = USART3_IRQn,
#endif

#if (FAMILY_STM32F1 > 2)
    [4] = UART4_IRQn,
    [5] = UART5_IRQn,
#endif
};

static GPIO_TypeDef *const id2gpio[] = {
    GPIOA,
    GPIOB,
    GPIOC,
    GPIOD,
    GPIOE,
    GPIOF,
    GPIOG,
};

static void _isr_default(void) {}

static uart_isr_cb_fn uart_irq[] = {
    [1] = _isr_default,
    [2] = _isr_default,
#if (FAMILY_STM32F1 > 1)
    [3] = _isr_default,
#endif

#if (FAMILY_STM32F1 > 2)
    [4] = _isr_default,
    [5] = _isr_default,
#endif
};

void USART1_IRQHandler(void)
{
    uart_irq[1]();
}
void USART2_IRQHandler(void)
{
    uart_irq[2]();
}

#if (FAMILY_STM32F1 > 1)
void USART3_IRQHandler(void)
{
    uart_irq[3]();
}
#endif

#if (FAMILY_STM32F1 > 2)
void UART4_IRQHandler(void)
{
    uart_irq[4]();
}
void UART5_IRQHandler(void)
{
    uart_irq[5]();
}
#endif

void drv_uart_pin_configure_txd(hal_id_t id, uint8_t pin)
{
    GPIO_TypeDef *const gpio = GPIO;
    switch (id)
    {
    case 0: // USART1:
    {
        if (pin == 0x16)
        {
            USART1_REMAP(1);
        }
        else
        {
            USART1_REMAP(0);
        }
        break;
    }
    case 1: // USART2:
    {
        if (pin == 0x35)
        {
            USART2_REMAP(1);
        }
        else
        {
            USART2_REMAP(0);
        }
        break;
    }

#if (FAMILY_STM32F1 > 1)
    case 2: // USART3:
    {
        if (pin == 0x2a)
        {
            USART3_REMAP(2);
        }
        else if (pin == 0x38)
        {
            USART3_REMAP(3);
        }
        else
        {
            AFIO->MAPR &= ~AFIO_MAPR_USART3_REMAP;
        }
        break;
    }
#endif

        // #if (FAMILY_STM32F1 > 2)
        //     case 3: // UART4:
        //     {
        //         if (pin == 0x0)
        //         {
        //             USART4_REMAP(1);
        //         }
        //         else
        //         {
        //             USART4_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 4: // UART5:
        //     {
        //         break;
        //     }
        //     case 5: // USART6:
        //     {
        //         if (pin == 0x6e)
        //         {
        //             USART6_TX_REMAP(1);
        //         }
        //         else
        //         {
        //             USART6_TX_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 6: // UART7:
        //     {
        //         if (pin == 0x57)
        //         {
        //             USART7_REMAP(1);
        //         }
        //         else
        //         {
        //             USART7_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 7: // UART8:
        //     {
        //         break;
        //     }
        // #endif

    default:
        return;
    }

    pin %= 16;
    int irq_nest = drv_irq_disable();
    writebits(&((__IO uint32_t *)&gpio->CRL)[pin / 8], pin % 8 * 4, 4, 0xb);
    drv_irq_enable(irq_nest);
}

void drv_uart_pin_configure_rxd(hal_id_t id, uint8_t pin)
{
    pin %= 16;
    switch (id)
    {
    case 0: // USART1:
    {
        if (pin == 0x17)
        {
            USART1_REMAP(1);
        }
        else
        {
            USART1_REMAP(0);
        }
        break;
    }
    case 1: // USART2:
    {
        if (pin == 0x36)
        {
            USART2_REMAP(1);
        }
        else
        {
            USART2_REMAP(0);
        }
        break;
    }

#if (FAMILY_STM32F1 > 1)
    case 2: // USART3:
    {
        if (pin == 0x2b)
        {
            USART3_REMAP(2);
        }
        else if (pin == 0x39)
        {
            USART3_REMAP(3);
        }
        else
        {
            SYS_LOG("io config error");
        }
        break;
    }
#endif

        // #if (FAMILY_STM32F1 > 2)
        //     case 3: // UART4:
        //     {
        //         if (pin == 0x01)
        //         {
        //             USART4_REMAP(1);
        //         }
        //         else
        //         {
        //             USART4_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 4: // UART5:
        //     {
        //         break;
        //     }
        //     case 5: // USART6:
        //     {
        //         if (pin == 0x69)
        //         {
        //             USART6_RX_REMAP(1);
        //         }
        //         else
        //         {
        //             USART6_RX_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 6: // UART7:
        //     {
        //         if (pin == 0x56)
        //         {
        //             USART7_REMAP(1);
        //         }
        //         else
        //         {
        //             USART7_REMAP(0);
        //         }
        //         break;
        //     }
        //     case 7: // UART8:
        //     {
        //     }
        // #endif

    default:
        return;
    }

    drv_gpio_pin_configure(pin, _GPIO_DIR_IN, _GPIO_PUD_PULL_UP);
}

void drv_uart_enable(hal_id_t id)
{
    switch ((int)DEV)
    {
    case 0: // USART1:
        __HAL_RCC_USART1_CLK_ENABLE();
        break;
    case 1: // USART2:
        __HAL_RCC_USART2_CLK_ENABLE();
        break;

#if (FAMILY_STM32F1 > 1)
    case 2: // USART3:
        break;
#endif

#if (FAMILY_STM32F1 > 2)
    case 3: // UART4:
        break;
    case 4: // UART5:
        break;
    case 5: // USART6:
        break;
    case 6: // UART7:
        break;
    case 7: // UART8:
        break;
#endif

    default:
        return;
    }
}

void drv_uart_disable(hal_id_t id)
{
    switch ((int)DEV)
    {
    case 0: // USART1:
        __HAL_RCC_USART1_CLK_DISABLE();
        break;
    case 1: // USART2:
        __HAL_RCC_USART2_CLK_DISABLE();
        break;
#if (FAMILY_STM32F1 > 1)
    case 2: // USART3:
        break;
#endif

#if (FAMILY_STM32F1 > 2)
#endif
    case 3: // UART4:
        break;
    case 4: // UART5:
        break;
    case 5: // USART6:
        break;
    case 6: // UART7:
        break;
    case 7: // UART8:
        break;

    default:
        return;
    }
}

void drv_uart_init(hal_id_t id, uart_param_t *param)
{
    if (id >= 0xFF)
    {
        return;
    }
}

void drv_uart_deinit(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return;
    }
}

void drv_uart_set_br(hal_id_t id, unsigned clk, unsigned baudrate)
{
    if (id >= 0xFF)
    {
        return;
    }

    DEV->BRR = (clk / 16 + baudrate / 2) / baudrate;
}

unsigned drv_uart_get_br(hal_id_t id, unsigned clk)
{
    if (id >= 0xFF)
    {
        return 0;
    }

    return (clk / 16) / DEV->BRR;
}

int drv_uart_poll_read(hal_id_t id, void *data)
{
    if (id >= 0xFF || drv_uart_is_rx_ready(id) == false)
    {
        return -1;
    }
    else
    {
        *((u8_t *)data) = DEV->DR;
        return 0;
    }
}

int drv_uart_poll_write(hal_id_t id, uint8_t data)
{
    if (id >= 0xFF)
    {
        return -1;
    }
    else
    {
        DEV->DR = data;
        return 0;
    }
}

int drv_uart_fifo_read(hal_id_t id, void *data, int size)
{
    int ret = 0;
    for (int i = 0; i < size; i++)
    {
        if (drv_uart_poll_read(id, &((char *)data)[i]))
        {
            break;
        }
        ++ret;
    }
    return ret;
}

int drv_uart_fifo_fill(hal_id_t id, const void *data, int size)
{
    if (id >= 0xFF)
    {
        return 0;
    }

    int ret = 0;
    for (uint ret = 0; ret < size; ret++)
    {
        drv_uart_poll_write(id, ((char *)data)[ret]);
        if (drv_uart_wait_tx_busy(id))
        {
            break;
        }
    }

    return ret;
}

int drv_uart_irq_callback_enable(hal_id_t id, uart_isr_cb_fn cb)
{
    if (id >= 0xFF)
    {
        return -1;
    }
    uart_irq[id] = cb;
    return 0;
}

int drv_uart_irq_callback_disable(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return -1;
    }
    uart_irq[id] = _isr_default;
    return 0;
}

void drv_uart_irq_tx_enable(hal_id_t id, int priority)
{
    if (id >= 0xFF)
    {
        return;
    }
    DEV->CR1 |= USART_CR1_TXEIE;
    NVIC_SetPriority(IRQ, priority);
    NVIC_EnableIRQ(IRQ);
}

void drv_uart_irq_tx_disable(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return;
    }
    DEV->CR1 &= ~USART_CR1_TXEIE;
}

void drv_uart_irq_rx_enable(hal_id_t id, int priority)
{
    if (id >= 0xFF)
    {
        return;
    }
    DEV->CR1 |= USART_CR1_RXNEIE;
    NVIC_SetPriority(IRQ, priority);
    NVIC_EnableIRQ(IRQ);
}

void drv_uart_irq_rx_disable(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return;
    }
    DEV->CR1 &= ~USART_CR1_RXNEIE;
}

int drv_uart_wait_tx_busy(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return false;
    }

    return (bool)!(DEV->SR & USART_FLAG_TC);
}

bool drv_uart_is_tx_ready(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return false;
    }

    return (bool)!!(DEV->SR & USART_FLAG_TXE);
}

bool drv_uart_is_rx_ready(hal_id_t id)
{
    if (id >= 0xFF)
    {
        return false;
    }

    return (bool)!!(DEV->SR & USART_FLAG_RXNE);
}
